package com.mathcat.mobile.android.net;

import java.net.URI;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;

import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpRequestBase;

import android.util.Log;

import com.mathcat.mobile.android.util.DaemonThread;
import com.mathcat.mobile.android.util.Task;

/**
 * HttpLoader.
 * 
 * @author Mathcat
 * @version 1.1.0, 2011.03.14
 */
public class HttpLoader {

	private final static String LOG_TAG = "http";
	
	private static Hashtable<String, HttpLoader> loaders = new Hashtable<String, HttpLoader>();
	
	public static HttpLoader newLoader(String loaderName, int loaderThreadsCount) {
		return HttpLoader.newLoader(loaderName, loaderThreadsCount, false, false);
	}
	
	public static HttpLoader newLoader(String loaderName, int loaderThreadsCount, boolean useCache, boolean useWaitingList) {
		if (loaderThreadsCount <= 0)
			throw new IllegalArgumentException("Assigning negative loader threads count!");
		if (loaderName == null)
			throw new NullPointerException("Loader name must not be null!");
		if (getLoader(loaderName) != null)
			throw new IllegalArgumentException("Loader already exists!");
		
		HttpLoader ret = new HttpLoader(loaderName, loaderThreadsCount);
		
		if (useCache) {
			
		}
		
		if (useWaitingList) {
			ret.useWaitingList = true;
			ret.waitingList = new HttpTransactionWaitingList();
		}
		
		synchronized (loaders) {
			loaders.put(loaderName, ret);
		}
		
		return ret;
	}
	
	public static HttpLoader getLoader(String loaderName) {
		if (loaderName == null)
			throw new NullPointerException("Loader name must not be null!");
		
		return loaders.get(loaderName);
	}
	
	public static void destroyAll() {
		Enumeration<HttpLoader> elements = loaders.elements();
		while (elements.hasMoreElements()) {
			HttpLoader loader = elements.nextElement();
			loader.destroy();
		}
	}
	
	private String loaderName;
	
	private int loaderThreadsCount;
	
	private DaemonThread[] loaderThreads;
	
	private ArrayList<Task> httpTransactionList = new ArrayList<Task>();
	
	final static int DEFAULT_TIMEOUT = 60000;
	int timeout = DEFAULT_TIMEOUT;
	
	boolean useCache = false;
	
	boolean useWaitingList = false;

	HttpTransactionWaitingList waitingList;
	
	private HttpLoader(String loaderName, int loaderThreadsCount) {
		this.loaderName = loaderName;
		this.loaderThreadsCount = loaderThreadsCount;
		
		this.loaderThreads = new DaemonThread[loaderThreadsCount];
		for (int i = 0; i < loaderThreadsCount; i++) {
			this.loaderThreads[i] = DaemonThread.create(Thread.NORM_PRIORITY, true, this.httpTransactionList);
		}
	}
	
	public void setTimeout(int timeout) {
		this.timeout = timeout;
	}
	
	public int getTimeout() {
		return timeout;
	}
	
	public void destroy() {
		ArrayList<Task> list = this.httpTransactionList;
		synchronized (list) {
			list.clear();
		}
		for (DaemonThread dt : this.loaderThreads) {
			dt.destroy();
		}
	}
	
	public void load(HttpRequestBase httpRequest, HttpCaller caller) {
		HttpTransaction transaction = createHttpTransaction(httpRequest, caller, this);
		
		if (this.useWaitingList && HttpGet.METHOD_NAME.equals(httpRequest.getMethod())) {
			synchronized (waitingList) {
				URI uri = httpRequest.getURI();
				if (waitingList.contains(uri)) {
					Log.d(LOG_TAG, "Request waiting, same request exists in waiting list; request uri: " + httpRequest.getURI());
					
					waitingList.add(uri, transaction);
					transaction.status = HttpTransaction.STATUS_WAITING;
					return;
				}
				
				waitingList.add(uri, transaction);
			}
		}
		
		ArrayList<Task> list = this.httpTransactionList;
		synchronized (list) {
			transaction.status = HttpTransaction.STATUS_WAITING;
			list.add(transaction);
			list.notify();
		}
	}
	
	public boolean cancelHttpRequest(HttpRequestBase httpRequest) {
		ArrayList<Task> list = this.httpTransactionList;
		boolean res = false;
		
		for (DaemonThread dt : this.loaderThreads) {
			Task task = dt.getCurrentTask();
			if (task != null && ((HttpTransaction) task).request == httpRequest) {
				task.cancel();
				res = true;
			}
		}
		
		if (!res) {
			Task toCancel = null;
			synchronized (list) {
				for (int i = 0; i < list.size(); ++i) {
					HttpTransaction transaction = (HttpTransaction) list.get(i);
					if (transaction.request == httpRequest) {
						toCancel = transaction;
						list.remove(i);
					}
				}
			}
			if (toCancel != null) {
				toCancel.cancel();
				res = true;
			}
		}
		
		return res;
	}
	
	public void cancelAllHttpRequest() {
		ArrayList<Task> list = this.httpTransactionList;
		
		for (DaemonThread dt : this.loaderThreads) {
			Task task = dt.getCurrentTask();
			if (task != null) {
				task.cancel();
			}
		}
		
		synchronized (list) {
	 		for (Task task : list) {
				task.cancel();
			}
	 		list.clear();
		}
	}

	private static HttpTransaction createHttpTransaction(HttpRequestBase httpRequest, HttpCaller httpCaller, HttpLoader httpLoader) {
		if (httpRequest == null)
			throw new NullPointerException("HTTP request must not be null!");
		
		HttpTransaction transaction = new HttpTransaction(httpRequest, httpCaller, httpLoader);
		return transaction;
	}

}
